﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using AgileShare.Framework.Core;

namespace AgileShare.Framework.Library.Helpers
{
	public class SPContentTypeHelper
	{
		public static SPContentTypeId GetContentTypeId(SPContentTypeId parent, Guid contentTypeGuid)
		{
			string contentTypeIdString = string.Format("{0}00{1}", parent.ToString(), contentTypeGuid.ToString("N"));
			SPContentTypeId contentTypeId = new SPContentTypeId(contentTypeIdString);
			return contentTypeId;
		}

		/// <summary>
		/// Ensures the type of the content.
		/// </summary>
		/// <param name="contentTypeId">The content type id.</param>
		/// <param name="name">The name.</param>
		/// <param name="contentTypeCollection">The content type collection.</param>
		/// <returns></returns>
		public static SPContentType EnsureContentType(SPContentTypeId contentTypeId, string name, SPContentTypeCollection contentTypeCollection)
		{
			SPContentType contentType = contentTypeCollection[contentTypeId];

			if (contentType == null)
			{
				contentType = new SPContentType(contentTypeId, contentTypeCollection, name);
				contentTypeCollection.Add(contentType);
				contentType = contentTypeCollection[contentTypeId];
			}

			return contentType;
		}

		/// <summary>
		/// Adds the content type.
		/// </summary>
		/// <param name="contentTypeId">The content type id.</param>
		/// <param name="list">The list.</param>
		/// <param name="site">The site.</param>
		/// <returns></returns>
		public static SPContentType AddSiteContentTypeToList(SPContentTypeId contentTypeId, SPList list, SPSite site)
		{
			SPWeb web = site.RootWeb;

			SPContentType listContentType = GetContentTypeByParentContentTypeId(contentTypeId, list.ContentTypes);
			SPContentType siteContentType = web.ContentTypes[contentTypeId];

			if (listContentType == null && siteContentType != null)
			{
				list.ContentTypes.Add(siteContentType);
			}

			return siteContentType;
		}

		/// <summary>
		/// Adds the site content types to list.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="site">The site.</param>
		/// <param name="contentTypeIds">The content type ids.</param>
		public static void AddSiteContentTypesToList(SPList list, SPSite site, bool replaceExisting, params SPContentTypeId[] contentTypeIds)
		{
			if (contentTypeIds.Any())
			{
				if (replaceExisting)
				{
					// remove all content types
					var contentTypes = list.ContentTypes.Cast<SPContentType>();
					foreach (var contentType in contentTypes)
					{
						list.ContentTypes.Delete(contentType.Id);
					}
				}

				foreach (SPContentTypeId contentTypeId in contentTypeIds)
				{
					AddSiteContentTypeToList(contentTypeId, list, site);
				}
			}
		}		
		
		/// <summary>
		/// Deletes the ContentType.
		/// </summary>
		/// <param name="contentTypeId">The content type id.</param>
		/// <param name="web">The web.</param>
		public static void DeleteContentType(SPContentTypeId contentTypeId, bool deleteIfHasUsages, SPSite site)
		{
			SPWeb web = site.RootWeb;
			SPContentType contentType = web.ContentTypes[contentTypeId];

			if (contentType != null)
			{
				// TODO: Should this search for templates and any list instance created with the contenttype and delete them?
				var usages = SPContentTypeUsage.GetUsages(contentType);

				if (usages.Count == 0 || deleteIfHasUsages)
				{
					try
					{
						foreach (SPContentTypeUsage usage in usages)
						{
							if (usage.IsUrlToList)
							{
								SPList list = web.GetList(usage.Url);
								RemoveContentType(usage.Id, list);
							}
							else
							{
								DeleteContentType(usage.Id, deleteIfHasUsages, site);
							}
						}

						contentType.ReadOnly = false;
						contentType.Sealed = false;
						web.ContentTypes.Delete(contentTypeId);
					}
					catch (SPException ex)
					{
						string errorMessage = string.Format("Could not delete ContentType with name {0} and id {1}'.", contentType.Name, contentTypeId);
						TraceProvider.Log(errorMessage, TraceProviderCategories.Default, (uint)TraceProviderEventIds.SPContentTypeHelper, TraceProviderSeverity.Warning, ex);
					}
				}
			}
		}

		/// <summary>
		/// Deletes the ContentType.
		/// </summary>
		/// <param name="web">The web.</param>
		/// <param name="contentTypeIds">The content type ids.</param>
		public static void DeleteContentTypes(SPSite site, bool deleteIfHasUsages, params SPContentTypeId[] contentTypeIds)
		{
			foreach (SPContentTypeId contentTypeId in contentTypeIds)
			{
				DeleteContentType(contentTypeId, deleteIfHasUsages, site);
			}
		}

		/// <summary>
		/// Removes the content type.
		/// </summary>
		/// <param name="contentTypeId">The content type id.</param>
		/// <param name="list">The list.</param>
		public static void RemoveContentType(SPContentTypeId contentTypeId, SPList list)
		{
			SPContentType contentType = GetContentTypeByParentContentTypeId(contentTypeId, list.ContentTypes);
			if (contentType != null)
			{
				contentType.ReadOnly = false;
				contentType.Sealed = false;
				list.ContentTypes.Delete(contentType.Id);
			}
		}

		/// <summary>
		/// Removes the content types.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="contentTypeIds">The content type ids.</param>
		public static void RemoveContentTypes(SPList list, params SPContentTypeId[] contentTypeIds)
		{
			foreach (SPContentTypeId contentTypeId in contentTypeIds)
			{
				RemoveContentType(contentTypeId, list);
			}
		}

		/// <summary>
		/// Adds fields to a content type.
		/// </summary>
		/// <param name="contentType">The content type.</param>
		/// <param name="fields">The fields to add.</param>
		public static void AddFieldsToExistingContentType(SPContentType contentType, params SPField[] fields)
		{
			using (SPWeb oWeb = contentType.ParentWeb.Site.OpenWeb(contentType.ParentWeb.ID))
			{
				SPContentType oContentType = contentType.ParentList.ContentTypes[contentType.Id];
				bool fieldsAdded = false;
				foreach (SPField field in fields)
				{
					if (!oContentType.Fields.Contains(field.Id))
					{
						oContentType.FieldLinks.Add(new SPFieldLink(field));
						fieldsAdded = true;
					}
				}
				if (fieldsAdded)
				{
					oContentType.Update();
				}
			}
		}

		/// <summary>
		/// Removes fields from a content type.
		/// </summary>
		/// <param name="contentType">The content type.</param>
		/// <param name="fields">The fields to remove.</param>
		public static void RemoveFieldsFromContentType(SPContentType contentType, params SPField[] fields)
		{
			using (SPWeb oWeb = contentType.ParentWeb.Site.OpenWeb(contentType.ParentWeb.ID))
			{
				SPContentType oContentType = contentType.ParentList.ContentTypes[contentType.Id];
				bool fieldsRemoved = false;
				foreach (SPField field in fields)
				{
					if (oContentType.Fields.Contains(field.Id))
					{
						oContentType.FieldLinks.Delete(field.Id);
						fieldsRemoved = true;
					}
				}
				if (fieldsRemoved)
				{
					oContentType.Update();
				}
			}
		}
		
		/// <summary>
		///  Searches the collection of content types and returns the SPContentType object that is the nearest match to the specified SPContentTypeId.
		/// </summary>
		/// <param name="baseContentTypeId">The content type id.</param>
		/// <param name="list">The list.</param>
		/// <returns></returns>
		public static SPContentType GetBestMatchContentType(SPContentTypeId baseContentTypeId, SPList list)
		{
			SPContentTypeId bestMatchContentTypeId = list.ContentTypes.BestMatch(baseContentTypeId);
			SPContentType bestMatchContentType = list.ContentTypes[bestMatchContentTypeId];
			return bestMatchContentType;
		}

		/// <summary>
		/// Gets the child content type of the parent content type id.
		/// </summary>
		/// <param name="parentContentTypeId">The parent content type id.</param>
		/// <param name="contentTypes">The content type collection.</param>
		/// <returns></returns>
		public static SPContentType GetContentTypeByParentContentTypeId(SPContentTypeId parentContentTypeId, SPContentTypeCollection contentTypes)
		{
			SPContentType contentType = contentTypes.Cast<SPContentType>().FirstOrDefault(x => x.Id.IsChildOf(parentContentTypeId));
			return contentType;
		}
	}
}
